/**
 * <vendor>
 *  Copyright 2009 Quanben Tech.
 * </vendor>
 */

using System;
using System.Text;


namespace QSharp.String.Compiler
{
    public class CharToken : IComparableToken
    {
        protected char myCh;

        public CharToken(char ch)
        {
            myCh = ch;
        }

        public char GetChar()
        {
            return myCh;
        }

        public static implicit operator char(CharToken token)
        {
            return token.myCh;
        }

        public int CompareTo(IComparableToken rhs)
        {
            CharToken charToken = rhs as CharToken;
            if (charToken != null)
            {
                return myCh.CompareTo(charToken.myCh);
            }
            NullToken nullToken = rhs as NullToken;
            if (nullToken != null)
            {
                return 1;
            }
            return -rhs.CompareTo(this);
        }

        public override string ToString()
        {
            return "'" + myCh + "'";
        }
    }

    /**
     * <remarks>
     *  This class may not be used since TextualTerminal_String does not require
     *  this type to be its token.
     * </remarks>
     */
    public class StringToken : IComparableToken
    {
        protected string myString;

        public StringToken(string s)
        {
            myString = s;
        }

        public string GetString()
        {
            return myString;
        }

        public static implicit operator string(StringToken token)
        {
            return token.myString;
        }

        public int CompareTo(IComparableToken rhs)
        {
            StringToken stringToken = rhs as StringToken;
            if (stringToken != null)
            {
                return myString.CompareTo(stringToken.myString);
            }
            CharToken charToken = rhs as CharToken;
            if (stringToken != null)
            {
                if (myString.Length < 1)
                {
                    return -1;
                }
                return myString[0].CompareTo(charToken);
            }
            NullToken nullToken = rhs as NullToken;
            if (nullToken != null)
            {
                return 1;
            }
            return -rhs.CompareTo(this);
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder('\'');
            sb.Append(myString);
            sb.Append('\'');
            return sb.ToString();
        }
    }


    /**
     * StringStream
     * <summary>
     *  A stream class that manipulate over a string and that is probably
     *  the most commonly used of all streams in QCompiler
     * </summary>
     */
    public class StringStream : TokenStream, ITokenStream<char>, ICloneableStream
    {
        public new class Position : TokenStream.Position
        {
            protected internal int myPos = 0;

            public override Object Clone()
            {
                Position pos = new Position();
                pos.myPos = myPos;
                return pos;
            }

            public override string ToString()
            {
                return myPos.ToString();
            }

            public int ToInt()
            {
                return myPos;
            }

            public override bool Equals(Object that)
            {
                Position thatPos = that as Position;
                if (that == null) return false;
                return this.myPos == thatPos.myPos;
            }

            public override int GetHashCode()
            {
                return base.GetHashCode();  /* we have no idea rewriting the method */
            }
        }

        protected string myString = "";
        protected Position myPosition = new Position();
        protected int myStringBegin = 0;
        protected int myStringEnd = 0;

        public StringStream(string s)
            : this(s, 0, s.Length)
        {
        }

        /*
         * this constructor allows the underlying string
         * to be a substring
         */
        public StringStream(string s, int begin, int end)
        {
            myString = s;
            myStringBegin = begin;
            myStringEnd = end;
            myPosition.myPos = begin;
        }


        public Object Clone()
        {
            StringStream ss = new StringStream(this.myString);
            ss.myPosition = (Position)this.myPosition.Clone();
            return ss;
        }

        public override IToken Read()
        {
            if (myPosition.myPos >= myStringEnd)
            {
                myPosition.myPos = myStringEnd;
                return null;
            }
            else if (myPosition.myPos < myStringBegin)
            {
                myPosition.myPos = myStringBegin;
                return null;
            }
            else
            {
                return new CharToken(myString[myPosition.myPos]);
            }
        }

        char ITokenStream<char>.Read()
        {
            // no checking performed for speed
            return myString[myPosition.myPos];
        }


        public override TokenStream.Position Tell()
        {
            return myPosition;
        }

        public override int Move(int nSteps)
        {
            int nOldPos = myPosition.myPos;
            myPosition.myPos += nSteps;
            if (myPosition.myPos < myStringBegin)
            {
                myPosition.myPos = myStringBegin;
            }
            if (myPosition.myPos > myStringEnd)
            {
                myPosition.myPos = myStringEnd;
            }
            return (myPosition.myPos - nOldPos);
        }

        public override void Seek(TokenStream.Position pos)
        {
            myPosition = (Position)pos;
            if (myPosition.myPos >= myStringEnd)
            {
                myPosition.myPos = myStringEnd;
            }
        }

        public override TokenStream.Position Pos
        {
            get { return Tell(); }
            set { Seek(value); }
        }

        public override string ToString()
        {
            if (myPosition.myPos < myStringBegin || 
                myPosition.myPos >= myStringEnd)
            {
                return "";
            }
            return myString.Substring(myPosition.myPos, 
                myStringEnd - myPosition.myPos);
        }

#if TEST_String_Compiler_StringStream
        public static void Main(string[] args)
        {
            string testString = "This is a test 5tring w1th s0me n0n-letter character5.";
            StringStream ss = new StringStream(testString);

            while (true)
            {
                CharToken token = (CharToken)ss.Read();
                if (token == null)
                {
                    break;
                }
                char ch = token;
                Console.WriteLine("ch = {0}", ch);
                ss.Move(1);
            }
        }
#endif
    }

    /**
     * StringsStream
     * <summary>
     *  A stream class that manipulates over an array of string
     * </summary>
     */
    public class StringsStream : TokenStream
    {
        public new class Position : TokenStream.Position
        {
            protected internal int myRow = 0;
            protected internal int myCol = 0;

            public override Object Clone()
            {
                Position pos = new Position();
                pos.myRow = myRow;
                pos.myCol = myCol;
                return pos;
            }

            public override string ToString()
            {
                StringBuilder sb = new StringBuilder('(');
                sb.Append(myRow.ToString());
                sb.Append(',');
                sb.Append(myCol.ToString());
                sb.Append(')');
                return sb.ToString();
            }
        }

        protected string[] myStrings = null;
        protected Position myPosition = null;

        public StringsStream(string[] ss)
        {
            myStrings = (string[])ss.Clone();
            for (int i = 0; i < myStrings.Length; i++)
            {
                StringBuilder sb = new StringBuilder(myStrings[i]);
                sb.Append('\n');
                myStrings[i] = sb.ToString();
            }
            myPosition = new Position();
        }

        public override IToken Read()
        {
            if (myPosition.myRow >= myStrings.Length)
            {
                return null;
            }
            return new CharToken(myStrings[myPosition.myRow][myPosition.myCol]);
        }

        public override TokenStream.Position Tell()
        {
            return myPosition;
        }

        public override void Seek(TokenStream.Position pos)
        {
            myPosition = (Position)pos;
        }

        public override int Move(int nSteps)
        {
            int nTotalSteps = 0;
            if (nSteps > 0)
            {
                while (true)
                {
                    if (myPosition.myRow >= myStrings.Length)
                    {
                        return nTotalSteps;
                    }
                    int nCurrStep = nSteps - nTotalSteps;
                    int nStepsAvailable = myStrings[myPosition.myRow].Length - myPosition.myCol;
                    if (nCurrStep < nStepsAvailable)
                    {
                        myPosition.myCol += nCurrStep;
                        nTotalSteps += nCurrStep;
                        return nTotalSteps;
                    }
                    nTotalSteps += nStepsAvailable;
                    myPosition.myCol = 0;
                    myPosition.myRow++;
                }
            }
            else
            {
                nSteps = -nSteps;
                while (true)
                {
                    int nCurrStep = nSteps - nTotalSteps;
                    int nStepsAvailable = myPosition.myCol;
                    if (nCurrStep <= nStepsAvailable)
                    {
                        myPosition.myCol -= nCurrStep;
                        nTotalSteps += nCurrStep;
                        return -nTotalSteps;
                    }
                    myPosition.myCol = 0;
                    nTotalSteps += nStepsAvailable;
                    if (myPosition.myRow == 0)
                    {
                        return -nTotalSteps;
                    }
                    myPosition.myRow--;
                    myPosition.myCol = myStrings[myPosition.myRow].Length;
                }
            }
        }

        public override TokenStream.Position Pos
        {
            get { return Tell(); }
            set { Seek(value); }
        }

#if TEST_String_Compiler_StringsStream
        public static void Main(string[] args)
        {
            string[] testStrings = new string[3]{
                "This is a test 5tring w1th s0me n0n-letter character5.",
                "This is the 2nd string",
                "! Goodbye, cruel world"};
            StringsStream sss = new StringsStream(testStrings);

            while (true)
            {
                CharToken token = (CharToken)sss.Read();
                if (token == null)
                {
                    break;
                }
                char ch = token;
                Console.Write("{0}", ch);
                sss.Move(1);
            }
        }
#endif
    }
}   /* namespace QSharp.String.Compiler */

